在动态库中如何调用外部函数(注册回调函数)

您所在的位置:网站首页 未定义function handle 在动态库中如何调用外部函数(注册回调函数)

在动态库中如何调用外部函数(注册回调函数)

#在动态库中如何调用外部函数(注册回调函数)| 来源: 网络整理| 查看: 265

一、背景介绍

在程序开发过程中,动态库可以需要调用其它外部模块中的函数,以便进行数据交互,减少代码重复等功能

本章解决的问题是:动态库在执行过程中如何到其它外部模块里调用函数?

在调用过程中需要考虑一个问题,外部模块的函数在哪里呢?怎么才能找到它藏在内存的哪个地址?

解决办法:其它外部模块向动态库注册回调函数!

二、程序测试 2.1 动态库 2.1.1 源码 // g++ lib.cpp -fPIC -shared -o lib.so -lrt -lpthread #include #include #include typedef void (*pf_void)(void); // 默认实现,必须有,否则线程创建会报错 extern "C" void func_in_main_def(void){ printf("the main is lazy, do NOT register me! \n"); } // 定义外部函数指针,需先定义函数再定义指针 pf_void func_in_main = func_in_main_def; extern "C" void mytimer(void){ int cnt =0; while (1) { printf("lib is runing %d !\n",cnt++); func_in_main(); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } } // 需先定义mytimer函数,再创建mytimer线程 std::thread sub_thread(mytimer); extern "C" void register_func(pf_void pf){ func_in_main = pf; } extern "C" int func_in_lib(int k){ printf("func_in_lib is called \n"); if (func_in_main) func_in_main(); return k + 1; } 2.1.2 编译

编译: g++ lib.cpp -fPIC -shared -o lib.so -lrt -lpthread

2.2 外部模块 2.2.1 源码 // g++ registermain.cpp -ldl -o registermain #include #include #include #include #include #include typedef void (*pf_void)(void); typedef int (*pfunc)(int); typedef int (*pregister)(pf_void); void func_in_main(void){ printf("func_in_main \n"); } int main(int argc, char *agv[]){ int a = 1; int b; // 打开动态库 printf("press key to pass! \n"); void *handle = dlopen("./lib.so", RTLD_NOW); // char ch =getchar(); // 测试动态库中子线程运行时序,在加载动态库即开始运行子线程 if (handle){ // 查找动态库中的注册函数 pregister register_func = (pregister) dlsym(handle, "register_func"); if (register_func){ register_func(func_in_main); } // 查找动态库中的函数 pfunc func = (pfunc) dlsym(handle, "func_in_lib"); if (func){ b = func(a); printf("b = %d \n", b); } else{ printf("dlsym failed! \n"); } } else{ printf("dlopen failed! \n"); } while (1){ printf("main is runing! \n"); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } dlclose(handle); return 0; } 2.2.2 编译与运行

编译: g++ registermain.cpp -ldl -o registermain

运行: ./registermain

输出:

func_in_lib is called func_in_main b = 2 main is runing! lib is runing 0 ! func_in_main main is runing! lib is runing 1 ! 三、代码解释 3.1 流程图

名词解释:

注册函数:注册函数register_func()在动态库中定义,在外部模块中使用。 回调函数:被动态库调用的外部模块中的函数,此处是func_in_main。 注册回调函数:一种行为,程序通过dlopen加载动态库,将自己的函数向动态库注册,让动态库可以调用自己的函数;有时也指注册函数。

整个程序运行过程中,外部模块与动态库的交互流程如下图:

其中粉色为外部模块执行代码,绿色为动态库执行代码,红色代表线程,该程序运行时外部模块执行主线程,在加载动态库后,动态库创建子线程。

注意:

在实际测试中,通过dlopen加载动态库后,紧接着通过getchar()函数阻塞主线程,会发现终端打印the main is lazy, do NOT register me!,因此得出结论:在dlopen()加载动态库时,初始化全局变量,动态库的子线程就开始运行了。

若未阻塞主线程并且主线程成功注册回调函数后,子线程中的函数指针func_in_main未执行动态库中的默认回调函数func_in_main_def,而是执行主线程中的func_in_main函数。(func_in_main在子线程为函数指针变量,在主线程中为函数名称)

博主仍有一个困惑,主线程在加载动态库之后与执行注册回调函数前(中间不阻塞),子线程有没有可能调用的是动态库中的默认回调函数func_in_main_def。(博主将子线程延迟改为1ms,仍然是执行的主线程的回调函数!本质是探究关于子线程启动顺序的问题。)

3.2 动态库

动态库中func_in_main和sub_thread都是全局变量,前者用来保存外部模块中的回调函数,以便在动态库的不同地方使用,后者是子线程。

该动态库的目的是创建一个线程,定期调用外部(主程序)注册的函数。默认情况下,线程将调用func_in_main_def,但主程序可以通过调用register_func来注册自己的函数。func_in_lib函数也可以被主程序调用,以便在动态库内部执行注册的外部函数。

动态库被调用时会创建一个子线程,该子线程每隔1000ms循环执行。若外部函数注册回调函数后,该子线程就会将默认的函数替换为外部的回调函数进行执行。

外部函数也可以通过特定的函数,手动调用动态库中得函数func_in_lib进行回调函数处理。

3.3 外部模块

外部模块通过typedef定义了多种类型函数指针:

typedef void (*pf_void)(void); typedef int (*pfunc)(int); typedef int (*pregister)(pf_void);

然后通过dlopen、dlsym等函数加载动态库,并通过register_func函数向动态库注册自己的回调函数func_in_main,然后利用func_in_lib来处理自己的回调函数。

参考: 干货 | 在动态库中如何调用外部函数? https://www.eet-china.com/mp/a51759.html



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3